package org.hentor.modules.generate.service.impl;

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.hentor.common.exception.HentorAdminException;
import org.hentor.common.util.RedisUtil;
import org.hentor.common.util.SpringContextUtils;
import org.hentor.modules.generate.dto.*;
import org.hentor.modules.generate.generate.constants.GenerateFileType;
import org.hentor.modules.generate.generate.dbGenerate.DbGenerateFactory;
import org.hentor.modules.generate.generate.enums.GenerateModelType;
import org.hentor.modules.generate.generate.fileGenrate.FileGenerateFactory;
import org.hentor.modules.generate.mapper.GenerateDevMainMapper;
import org.hentor.modules.generate.service.IGenerateDevDetailService;
import org.hentor.modules.generate.service.IGenerateDevMainService;
import org.hentor.modules.generate.utils.KeywordValidator;
import org.hentor.modules.generate.vo.GenerateCodeVO;
import org.hentor.generate.entity.GenerateDevDetail;
import org.hentor.generate.entity.GenerateDevMain;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 代码生成主表 服务实现类
 * </p>
 *
 * @author wzd
 * @since 2023-06-12
 */
@Service
@Slf4j
public class GenerateDevMainServiceImpl extends ServiceImpl<GenerateDevMainMapper, GenerateDevMain> implements IGenerateDevMainService {

    @Autowired
    private IGenerateDevDetailService generateDevDetailService;

    @Autowired
    private GenerateDevMainMapper generateDevMainMapper;

    @Autowired
    private RedisUtil redisUtil;
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveGenerateTable(GenerateDevSaveDTO generateDevSaveDTO) {
        //表信息
        GenerateDevMain generateDevMain = generateDevSaveDTO.getGenerateDevMain();
        //字段信息
        List<GenerateDevDetail> generateDevDetail = generateDevSaveDTO.getGenerateDevDetail();
        if(StringUtils.isBlank(generateDevMain.getTableName())){
            throw new HentorAdminException("保存失败，表名不能为空！");
        }
        //校验表名是否存在
        LambdaQueryWrapper<GenerateDevMain> generateDevMainLambdaQueryWrapper = new LambdaQueryWrapper<>();
        generateDevMainLambdaQueryWrapper.eq(GenerateDevMain::getTableName, generateDevMain.getTableName());
        List<GenerateDevMain> list = list(generateDevMainLambdaQueryWrapper);
        if(CollectionUtils.isNotEmpty(list)){
            throw new HentorAdminException(String.format("保存失败，表名%s已存在！", generateDevMain.getTableName()));
        }
        //校验字段
        checkColumnName(generateDevDetail);
        //保存主表信息
        save(generateDevMain);

        //排序不能重复
        generateDevDetail.forEach(x->{
            //关联主表id
            x.setMainId(generateDevMain.getId());
            if(StringUtils.isBlank(x.getColumnName())){
                throw new HentorAdminException("保存失败，字段名不能为空！");
            }

        });
        generateDevDetailService.saveBatch(generateDevDetail);
    }

    private void checkColumnName(List<GenerateDevDetail> generateDevDetail){
        //校验字段名不能为关键字
        List<String> keyWordList = generateDevDetail.stream().map(GenerateDevDetail::getColumnName).filter(KeywordValidator::isKeyword).collect(Collectors.toList());
        if(CollectionUtils.isNotEmpty(keyWordList)){
            throw new HentorAdminException("字段名:"+String.join(",",keyWordList)+"不能为关键字！");
        }
        //字段名不能重复
        Map<String, List<GenerateDevDetail>> collect = generateDevDetail.stream().collect(Collectors.groupingBy(GenerateDevDetail::getColumnName));
        List<Map.Entry<String, List<GenerateDevDetail>>> collect1 = collect.entrySet().stream().filter(x -> collect.get(x.getKey()).size() > 1).collect(Collectors.toList());
        if(CollectionUtils.isNotEmpty(collect1)){
            throw new HentorAdminException(String.format("字段名%s重复！",collect1.get(0).getKey()));
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void syncDataBase(GenerateDevMain generateDevMain) throws SQLException {
        log.info("=========================同步数据库：开始同步数据结构=========================");
        //查询表信息
        GenerateDevMain byId = getById(generateDevMain);
        if(byId == null){
            throw new HentorAdminException("同步失败，表单配置信息不存在！");
        }
        LambdaQueryWrapper<GenerateDevDetail> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(GenerateDevDetail::getMainId, generateDevMain.getId());
        List<GenerateDevDetail> list = generateDevDetailService.list(queryWrapper);
        GenerateDbDTO generateDbDTO = new GenerateDbDTO();
        generateDbDTO.setGenerateDevMain(byId);
        generateDbDTO.setGenerateDevDetailList(list);
        //暂时只支持mysql
        generateDbDTO.setDbType(DbType.MYSQL);
        String[] a = new String[]{"TABLE"};
        Connection connection = getConnection();
        ResultSet tables = connection.getMetaData().getTables(connection.getCatalog(), null, byId.getTableName(), a);
        //判断表名是否存在
        if(tables.next()){
            //修改表备注
            generateDbDTO.setModel(GenerateModelType.UPDATE_TABLE_COMMENT.getCode());
            //获取ddl语句
            List<String> generate = DbGenerateFactory.generate(generateDbDTO);
            List<GenerateDevDetail> generateDevDetails = getChangeColumnList(connection, generateDbDTO);
            //获取要修改的列信息
            generateDbDTO.setGenerateDevDetailList(generateDevDetails);
            //更新字表信息
            generateDevDetailService.updateBatchById(generateDevDetails);
            generateDbDTO.setModel(GenerateModelType.UPDATE_COLUMN_NAME.getCode());
            generate.addAll(DbGenerateFactory.generate(generateDbDTO));
            //修改字段属性
            generateDbDTO.setModel(GenerateModelType.UPDATE_COLUMN_TYPE.getCode());
            generateDbDTO.setGenerateDevDetailList(generateDevDetailService.list(queryWrapper));
            generate.addAll(DbGenerateFactory.generate(generateDbDTO));
            //判断主键是否有修改
            ArrayList<String> oldPrimaryKeyList = new ArrayList<>();
            ResultSet oldPrimaryKeys = connection.getMetaData().getPrimaryKeys(connection.getCatalog(), null, generateDbDTO.getGenerateDevMain().getTableName());
            while (oldPrimaryKeys.next()){
                String columnName = oldPrimaryKeys.getString("COLUMN_NAME");
                oldPrimaryKeyList.add(columnName);
            }
            List<String> newPrimaryKeyList = generateDbDTO.getGenerateDevDetailList().stream().filter(x -> 1 == x.getPrimaryKey()).map(GenerateDevDetail::getColumnName).collect(Collectors.toList());
            boolean flag = org.apache.commons.collections.CollectionUtils.isEqualCollection(oldPrimaryKeyList,newPrimaryKeyList);
            if(!flag){
                if(CollectionUtils.isNotEmpty(oldPrimaryKeyList)){
                    //删除主键
                    generateDbDTO.setModel(GenerateModelType.DELETE_PRIMARY_KEY.getCode());
                    //获取ddl语句
                    generate.addAll(DbGenerateFactory.generate(generateDbDTO));
                }
                //创建主键
                generateDbDTO.setModel(GenerateModelType.CREATE_PRIMARY_KEY.getCode());
                //获取ddl语句
                generate.addAll(DbGenerateFactory.generate(generateDbDTO));
            }
            generate.forEach(x->{
                generateDevMainMapper.generateDll(x);
            });
        }else {
            //创建表信息
            generateDbDTO.setModel(GenerateModelType.CREATE_TABLE.getCode());
            //获取ddl语句
            List<String> generate = DbGenerateFactory.generate(generateDbDTO);
            generateDevMainMapper.generateDllList(generate);
        }

        log.info("=========================同步数据库：同步成功=========================");
    }

    @Override
    public IPage<GenerateDevMain> selectGenerateTable(GenerateDevSelectDTO generateDevSelectDTO) {
        LambdaQueryWrapper<GenerateDevMain> generateDevMainLambdaQueryWrapper = new LambdaQueryWrapper<>();
        generateDevMainLambdaQueryWrapper.eq(StringUtils.isNotBlank(generateDevSelectDTO.getTableName()), GenerateDevMain::getTableName, generateDevSelectDTO.getTableName());
        return page(new Page<>(generateDevSelectDTO.getPageNo(), generateDevSelectDTO.getPageSize()),generateDevMainLambdaQueryWrapper);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateGenerateTable(GenerateDevSaveDTO generateDevSaveDTO) {
        List<GenerateDevDetail> generateDevDetail = generateDevSaveDTO.getGenerateDevDetail();
        //校验字段
        checkColumnName(generateDevDetail);
        if(generateDevSaveDTO.getGenerateDevMain() != null){
            updateById(generateDevSaveDTO.getGenerateDevMain());
        }
        if(CollectionUtils.isNotEmpty(generateDevDetail)){
            //全删全增
            LambdaQueryWrapper<GenerateDevDetail> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(GenerateDevDetail::getMainId, generateDevSaveDTO.getGenerateDevMain().getId());
            generateDevDetailService.remove(queryWrapper);
            generateDevDetail.forEach(x->x.setMainId(generateDevSaveDTO.getGenerateDevMain().getId()));
            generateDevDetailService.saveBatch(generateDevDetail);
        }

    }

    @Override
    public void deleteGenerateTable(GenerateDevMain generateDevMain) {
        if(StringUtils.isNotBlank(generateDevMain.getId())){
            removeById(generateDevMain);
            LambdaQueryWrapper<GenerateDevDetail> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(GenerateDevDetail::getMainId, generateDevMain.getId());
            generateDevDetailService.remove(queryWrapper);
        }
    }

    @Override
    public GenerateCodeVO codeFileGenerator(GenerateCodeDTO generateCodeDTO) {
        GenerateCodeVO generateCodeVO = new GenerateCodeVO();
        GenerateDevMain byId = getById(generateCodeDTO.getMainId());
        if(byId == null){
            throw new HentorAdminException("表单配置信息不存在！");
        }
        LambdaQueryWrapper<GenerateDevDetail> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(GenerateDevDetail::getMainId,generateCodeDTO.getMainId());
        List<GenerateDevDetail> list = generateDevDetailService.list(queryWrapper);
        GenerateFileDTO generateFileDTO = new GenerateFileDTO();
        generateFileDTO.setPackagePath(generateCodeDTO.getPackagePath());
        generateFileDTO.setFileType(GenerateFileType.JAVA_FILE);
        generateFileDTO.setGenerateDevMain(byId);
        generateFileDTO.setGenerateDevDetailList(list);
        //生成java后端代码
        List<String> javaGenerate = FileGenerateFactory.generate(generateFileDTO);
        generateFileDTO.setFileType(GenerateFileType.VUE_FILE);
        //前端代码
        List<String> vueGenerate = FileGenerateFactory.generate(generateFileDTO);
        UUID uuid = UUID.randomUUID();
        String uuidString = uuid.toString().replace("-", "");
        generateCodeVO.setFileKey(uuidString);
        generateCodeVO.setJavaPathList(javaGenerate);
        generateCodeVO.setVuePathList(vueGenerate);
        List<String> combined = new ArrayList<>();
        combined.addAll(javaGenerate);
        combined.addAll(vueGenerate);
        redisUtil.set("CODE_FILE_KEY_"+uuidString, combined,3600);
        return generateCodeVO;
    }

    /**
     * 获取变化的列信息
     * 先按照顺序匹配元数据oldval和页面录入的数据newval，按顺序分组放到一个list中，循环list，判断newval=oldval的拿出来，代表这些字段不需要做任何结构操作，但需要修改属性
     * @param connection
     * @param generateDbDTO
     * @return
     * @throws SQLException
     */
    private List<GenerateDevDetail> getChangeColumnList(Connection connection, GenerateDbDTO generateDbDTO) throws SQLException {
        //通过数据库元数据获取列信息
        ResultSet columns = connection.getMetaData().getColumns(connection.getCatalog(), null, generateDbDTO.getGenerateDevMain().getTableName(), null);
        ArrayList<GenerateDevDetail> generateDevColumns = new ArrayList<>();
        int i = 1;
        while (columns.next()){
            GenerateDevDetail generateDevColumn = new GenerateDevDetail();
            String columnName = columns.getString("COLUMN_NAME");
            generateDevColumn.setOldColumnName(columnName);
            generateDevColumn.setSortOrder(i);
            i++;
            generateDevColumns.add(generateDevColumn);
        }
        //合并新旧数据集合
        generateDevColumns.addAll(generateDbDTO.getGenerateDevDetailList());
        Map<Integer, List<GenerateDevDetail>> collect = generateDevColumns.stream().collect(Collectors.groupingBy(GenerateDevDetail::getSortOrder));
        ArrayList<GenerateDevDetail> generateDevDetails = new ArrayList<>();
        collect.keySet().forEach(x->{
            GenerateDevDetail generateDevDetail = new GenerateDevDetail();
            collect.get(x).forEach(y->{
                if (StringUtils.isNotBlank(y.getOldColumnName()) && StringUtils.isBlank(y.getColumnName())){
                    generateDevDetail.setOldColumnName(y.getOldColumnName());
                    if(generateDevDetail.getSortOrder() == null){
                        generateDevDetail.setSortOrder(y.getSortOrder());
                    }
                }else {
                    BeanUtils.copyProperties(y, generateDevDetail, "oldColumnName","sortOrder");
                    if(generateDevDetail.getSortOrder() == null){
                        generateDevDetail.setSortOrder(y.getSortOrder());
                    }
                }
            });
            generateDevDetails.add(generateDevDetail);
        });
        ArrayList<GenerateDevDetail> notInGenerateDevDetails = new ArrayList<>();
        generateDbDTO.getGenerateDevDetailList().forEach(x->{
            generateDevColumns.forEach(y->{
                if(x.getColumnName().equals(y.getOldColumnName())){
                    notInGenerateDevDetails.add(x);
                }
            });
        });
        generateDevDetails.removeAll(notInGenerateDevDetails);
        return generateDevDetails;
    }

    /**
     * 获取数据库连接
     * @return
     */
    private Connection getConnection() throws SQLException {
        DataSource dataSource = SpringContextUtils.getApplicationContext().getBean(DataSource.class);
        return dataSource.getConnection();
    }

}
